/*
 * main.c
 * Copyright (C) Nathan Holland & Andrew Zeng 2011
 * 
 * NBodySimulator is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * NBodySimulator is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "update.h"

/*Forward declarations*/
void parse(int argc, char *argv[]);
void invalid();
void create();
void setVel(double x, double y, double z, int body);
void create();
void scanLength();
void writeToCSV(int chunk, int maximum);

/*Variables*/
int length;
int bodies = 6;
int subLength;
double timestep;
double *pos, *vel, *acc, *mass;
FILE* file;

int main(int argc, char *argv[]) {
    parse(argc, argv);

    file = fopen(argv[1], "r");
    scanLength();

    /*Initialize variables*/
    subLength = 220;
    double *pos = (double *) malloc(3 * (subLength + 1) * bodies * sizeof (double)); //Of the form [x.y.z][step][body] 
    double *vel = (double *) malloc(3 * bodies * sizeof (double)); //Of the form [x.y.z][body]
    double *acc = (double *) malloc(3 * bodies * sizeof (double)); //Of the form [x.y.z][body]
    double *mass = (double *) malloc(bodies * sizeof (double));

    set(timestep, bodies, subLength + 1);
    setVar(mass, acc, vel, pos);


    create();

    file = fopen(argv[2], "w");

    int chunks;
    if (length % subLength == 0)
        chunks = length / subLength;
    else
        chunks = length / subLength + 1;
    fprintf(file, "%d,%d,%d,%d\n", bodies, length, chunks, subLength);
    /*Start Simulation*/
    int counter = length;
    int currentChunk = 0;
    int max = subLength;
    while (counter > 0) {
        for (int i = 0; i < max; i++) {
            for (int j = 0; j < bodies; j++) {
                update(j, i);
            }
        }

        if (counter > 2 * subLength) {
            counter = counter - subLength;
            writeToCSV(currentChunk, subLength);
        } else if (counter > subLength) {
            counter -= subLength;
            writeToCSV(currentChunk, subLength);
            max = counter;
        } else {
            writeToCSV(currentChunk, counter);
            counter -= subLength;
            break;

        }
        currentChunk++;
    }
    for (int i = 0; i < bodies; i++) {
        double output[3];
        getPos2(output, i, counter + subLength - 1);
        printf("%f,%f,%f\n", output[0], output[1], output[2]);
    }
    free(pos);
    free(vel);
    free(mass);
    free(acc);
    return 0;
}

void writeToCSV(int chunk, int maximum) {
    for (int i = 0; i < maximum; i++) {
        for (int j = 0; j < bodies; j++) {
            double *coords = getPos(j, i);
            fprintf(file, "%d,%d,%f,%f,%f\n", (chunk) * (subLength) + i, j, coords[0], coords[1], coords[2]);
        }
    }
    if (subLength == maximum)
        for (int j = 0; j < bodies; j++) {
            double coords[3];
            getPos2(coords, j, subLength - 1);
            setPos(coords[0], coords[1], coords[2], j, 0);
        }
}

void setVel(double x, double y, double z, int body) {
    int index = body;
    int mult = bodies;
    vel[index] = x;
    vel[index + mult] = y;
    vel[index + mult * 2] = z;
}

void parse(int argc, char *argv[]) {
    int time = 10;
    timestep = .001;
    /*  PARSE THE ARGUMENTS */
    if (argc < 2) {
        invalid();
    }

    if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
        if (argc == 2) {
            printf("REQUIRED ARGUMENTS:\n    'file'\n        A .csv file that "
                    "details the variables for the bodies");
            printf("\nOPTIONAL ARGUMENTS:");
            printf("    \\n-t\n    --time\n        Amount of time to run "
                    "the simulation\n");
        } else {
            if (strcmp(argv[2], "-t") == 0 || strcmp(argv[2], "--time") == 0) {
                printf("    -t\n    --time\n        Amount of time to run "
                        "the simulation\n");
            }
        }
    } else if (argc == 4) {
        invalid();
    }
    int i;
    for (i = 2; i < argc; i++) {
        if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--time") == 0) {
            time = atoi(argv[i + 1]);
        }
    }
    length = (int) (time / timestep);
}

void create() {
    while (1) {
        int num;
        double a, b, c, d, e, f, g;
        int res = fscanf(file, "%d,%lf,[%lf %lf %lf],[%lf %lf %lf]", &num, &a, &b, &c, &d, &e, &f, &g);
        if (res == EOF) {
            return;
        } else {
            setPos(b, c, d, num - 1, 0);
            setVel(e, f, g, num - 1);
            mass[num - 1] = a;
        }
    }
}

void scanLength() {
    fscanf(file, "%d", &bodies);
}

void invalid() {
    printf("Invalid arguments!\n");
}
